/*This file is part of WZLibCP.

 *  WZLibCP is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

 *  WZLibCP is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

 *  You should have received a copy of the GNU General Public License
    along with WZLibCP.  If not, see <http://www.gnu.org/licenses/>.
		
	Author: calc0000
*/
#include "stdafx.h"
#include "WZBinaryReader.h"

#include "WZCrypto.h"
#include "WZFileHeader.h"

using namespace std;

namespace WZLib{
	ctor WZBinaryReader::WZBinaryReader(const char *fPath):BinaryReader(fPath){
		//BinaryReader::BinaryReader(fPath);
		this->header=NULL;
	}
	ctor WZBinaryReader::WZBinaryReader(FILE* fp):BinaryReader(fp){
		//BinaryReader::BinaryReader(fp);
		this->header=NULL;
	}
	int WZBinaryReader::ReadCompressedInt(){
		signed char chk=this->ReadSByte();
		if(chk!=-128)
			return (int)chk;
		return this->ReadInt32();
	}
	string WZBinaryReader::ReadEncryptedString(){
		char smallLength=this->ReadSByte();
		if(smallLength==0)
			return "";
		string retString="";
		if(smallLength>=0){
			unsigned int mask=0xAAAA;
			unsigned int length;
			if(smallLength==0x7f)
				length=this->ReadUInt32();
			else
				length=smallLength;
			if(length<=0)
				return "";
			for(int i=0;i<length;i++){
				unsigned int encryptedChar=this->ReadUInt32();
				encryptedChar^=mask;
				encryptedChar^=(WZCrypto::WZKey[i*2+1]<<8)+WZCrypto::WZKey[i*2];
				retString+=(char)encryptedChar;
				mask++;
			}
		}else{
			unsigned int mask=0xAA;
			unsigned int length;
			if(smallLength==-128)
				length=this->ReadUInt32();
			else
				length=-smallLength;
			if(length<=0)
				return "";
			for(int i=0;i<length;i++){
				unsigned char encryptedChar=this->ReadByte();
				encryptedChar^=mask;
				encryptedChar^=WZCrypto::WZKey[i];
				retString+=(char)encryptedChar;
				mask++;
			}
		}
		return retString;
	}
	unsigned int WZBinaryReader::ReadOffset(){
		unsigned int offset=this->Position();
		offset=(offset-this->header->fileStart())^0xFFFFFFFF;
		offset*=this->header->versionHash();
		offset-=0x581C3F6D;
		offset=(offset<<(offset&0x1f))|(offset>>(32-offset&0x1F));
		offset^=this->ReadUInt32();
		offset+=this->header->fileStart()*2;
		return offset;
	}
	string WZBinaryReader::ReadStringAtOffset(long offset){
		long cur=this->Position();
		this->Position(offset);
		string ret=this->ReadEncryptedString();
		this->Position(cur);
		return ret;
	}
	string WZBinaryReader::ReadStringBlock(long offset){
		byte b=this->ReadByte();
		string ret="";
		long currentOffset=0;
		switch(b){
			case 0:
			case 0x73:
				ret=this->ReadEncryptedString();
				break;
			case 1:
			case 0x1B:
				offset+=this->ReadInt32();
				currentOffset=this->Position();
				this->Position(offset);
				ret=this->ReadEncryptedString();
				this->Position(currentOffset);
				break;
			default:
				ret="";
				break;
		}
		return ret;
	}
};